#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdlib>
#include<cstring>
#include <cassert>
using namespace std;
#include "rm.h"

RM* RM::_rm = 0;

#define intsize sizeof(int);

RM* RM::Instance()
{
    if(!_rm)
        _rm = new RM();
    
    return _rm;
}

RM::RM()
{
   pf=PF_Manager::Instance();
   int result=pf->CreateFile("Systemcatalog.bin");
   if(result==0)
   {
	   string names[]={"TableName","AttrName","AttrType","Attrlen","AttrPos"};
	   int lengths[]={20,20,4,4,4};
	   AttrType types[]={TypeVarChar,TypeVarChar,TypeInt,TypeInt,TypeInt};
	   enterIntoSysCatalog("Systemcatalog.bin",createVectorOfAttr(5,names,lengths,types));
   }
}

RM::~RM()
{
	delete _rm; //KAB
}

unsigned RM::strlen(char* str)
{
	unsigned len=0;
	while(*str++)
		len++;
	return len;
}

RC RM::enterIntoSysCatalog(char* tableName, vector<Attribute> attrs)
{
	if(isTableExists(tableName)==0)
	{
		//cout<<"Table already exists"<<endl;
		return -1;
	}
	pf->OpenFile("SystemCatalog.bin",fh);
	fseek(fh.localFilePtr,0,SEEK_END);
	int pos=ftell(fh.localFilePtr);

	for(int i=0;i<attrs.size();i++)
	{
		catalogEntry *ce=new catalogEntry;
		strcpy(ce->tableName,tableName);
		strcpy(ce->attrname,attrs[i].name.c_str());
		ce->length=attrs[i].length;
		ce->type=attrs[i].type;
		ce->attrPos=i+1;
		fseek(fh.localFilePtr,0,SEEK_END);
		fwrite((const void*)ce,1,sizeof(catalogEntry),fh.localFilePtr);
		delete ce;

	}
	pf->CloseFile(fh);

	return 0;
}

vector<Attribute> RM::createVectorOfAttr(int number,string names[],int lengths[],AttrType types[])
{
 vector<Attribute> toReturn;

 for(int i=0;i<number;i++)
 {
	 struct Attribute newAttr;
	 newAttr.name=names[i];
	 newAttr.length=lengths[i];
	 newAttr.type=types[i];
	 toReturn.push_back(newAttr);
 }
 return toReturn;
}

RC RM::isTableExists(char *tableName)
{
	char *data=new char[4096];
	pf->OpenFile("Systemcatalog.bin",fh);

	fseek(fh.localFilePtr,0,SEEK_END);
	int end=ftell(fh.localFilePtr);

	int numBlocks=end/sizeof(catalogEntry);
	//cout<<numBlocks<<endl;
	int blockcount=0;
	int pageblockcount=0;
	int pageRead=0;

	fseek(fh.localFilePtr,0,SEEK_SET);

	while(blockcount<numBlocks)
	{
		pageblockcount=0;
		fh.ReadPage(pageRead,data);
		char* iterator=data;
		while(pageblockcount<64 && blockcount<numBlocks)
		{
			catalogEntry *ce=new catalogEntry;
			memcpy((void*)ce,iterator,64);

			if(strcmp(ce->tableName,tableName)==0)
			{
				return 0;
			}
			delete ce;
			iterator=iterator+64;
			pageblockcount++;
			blockcount++;
		}
		pageRead++;
	}
	delete data;
	pf->CloseFile(fh);
	return -1;
}

RC RM::initializeNewPage(const char* fileName)
{
	char *data=new char[4096];
	char *iterator=data;
	int result;
	int toStore[]={3592,500,3592,-1,0};

	memset(data,'\0',4096);
    result=pf->OpenFile(fileName,fh);
	if(result==0)
    {
	   for(int i=0;i<4;i++)
	   {
		   memcpy(iterator,&toStore[i],sizeof(int));
		   iterator=iterator+sizeof(int);
	   }
	   iterator=data+4092;
	   memcpy(iterator,&toStore[4],sizeof(int));
	   fh.AppendPage(data);
	   pf->CloseFile(fh);
    }
	delete data;
}

RC RM::createTable(const string tableName, const vector<Attribute> &attrs)
{
	/*
	 * Pseudo code:
	 *
	 * Check if the system catalog has entry for tableName
	 *  	If yes,
	 *  		show error
	 *  	Else
	 *  		Create entry for tableName in system catalog with given attrs
	 *  		Create file for tableName.dat
	 *
	 */
	if(isTableExists((char*)tableName.c_str())==-1)
	{
		//cout<<"Table doesn't exist"<<endl;

		pf->CreateFile(tableName.c_str());
		//cout<<"created file"<<endl;


		initializeNewPage(tableName.c_str());
		//cout<<"Page initialized"<<endl;

		enterIntoSysCatalog((char*)tableName.c_str(),attrs);

		//cout<<"Reenter into sys catalog"<<endl;

		return 0;
	}
	//cout<<"Table exists already"<<endl;
	return -1;
}


RC RM::deleteTable(const string tableName)
{
	/*
	 * Pseudo code:
	 *
	 * Check if system table had entry for tableName
	 * 		If yes
	 * 			Remove entry for tableName
	 * 			Update free space manager of system catalog
	 * 			Remove the file tableName.dat
	 * 		Else
	 * 			Show error that table does not exist
	 */
		char *data=new char[4096];
		string filetoDel;
		bool todel=false;

		pf->OpenFile("Systemcatalog.bin",fh);

		fseek(fh.localFilePtr,0,SEEK_END);
		int end=ftell(fh.localFilePtr);

		//cout<<"end of sys cat "<<end<<endl;

		int numBlocks=end/sizeof(catalogEntry);

		//cout<<"Num Blocks: "<<numBlocks<<endl;

		int blockcount=0;
		int pageblockcount=0;
		int pageRead=0;

		fseek(fh.localFilePtr,0,SEEK_SET);

		while(blockcount<numBlocks)
		{
			pageblockcount=0;
			fh.ReadPage(pageRead,data);


			char* iterator=data;
			while(pageblockcount<64 && blockcount<numBlocks)
			{
				catalogEntry *ce=new catalogEntry;
				memcpy((void*)ce,iterator,64);

				if(strcmp(ce->tableName,tableName.c_str())==0)
				{
					todel=true;
					filetoDel=ce->tableName;
					memset(iterator,'\0',64);
				}
				delete ce;
				iterator=iterator+64;
				pageblockcount++;
				blockcount++;
			}
			fh.WritePage(pageRead,data);
			//cout<<"Page write success"<<endl;
			//cout<<"block count"<<blockcount<<endl;
			//cout<<"page block count"<<pageblockcount<<endl;

			pageRead++;
		}
		delete data;

		pf->CloseFile(fh);
		if(todel==true)
		{
			assert(pf->DestroyFile((const char*)filetoDel.c_str())==0);
			//cout<<"deletion successful"<<endl;
			return 0;
		}
		//cout<<"Table you are trying to delete does not exist"<<endl;
		return -1;

}


RC RM::getAttributes(const string tableName, vector<Attribute> &attrs)
{
	/**
	 * Check if system catalog has entry for tableName
	 * If yes,
	 * 		Scan all the records in system catalog which are for tableName
	 * 		Read the attribute information(type, nullable etc. ) about attribute and push in vector
	 * 		Do it for all the records found
	 * 		return
	 * Else
	 * 		Show error that the table does not exist
	 */

	char *data=new char[4096];
	bool tableFound=false;

	pf->OpenFile("Systemcatalog.bin",fh);

    fseek(fh.localFilePtr,0,SEEK_END);
	int end=ftell(fh.localFilePtr);
	int numBlocks=end/sizeof(catalogEntry);

	//cout<<tableName;
	//cout<<numBlocks;

	int blockcount=0;
	int pageblockcount=0;
	int pageRead=0;

	fseek(fh.localFilePtr,0,SEEK_SET);

	while(blockcount<numBlocks)
	{
		pageblockcount=0;
		fh.ReadPage(pageRead,data);
		char* iterator=data;
		while(pageblockcount<64 && blockcount<numBlocks)
		{
			catalogEntry *ce=new catalogEntry;
			memcpy((void*)ce,iterator,64);

			if(strcmp(ce->tableName,tableName.c_str())==0)
			{
				tableFound=true;
				Attribute obj;
				obj.length=ce->length;
				obj.name=ce->attrname;
				obj.type=(AttrType)ce->type;
				attrs.push_back(obj);
			}
			delete ce;
			iterator=iterator+64;
			pageblockcount++;
			blockcount++;
		}
		fh.WritePage(pageRead,data);
		pageRead++;
	}
	delete data;

	pf->CloseFile(fh);

	if(tableFound==true)
	{
		//cout<<"Attributes found"<<endl;
		return 0;
	}
	else
	{
		//cout<<"Table you are trying to find attributes of does not exist"<<endl;
		return -1;
	}

}

RC RM::ifRecordFitsInCurrPage(int pagenum,int totlen,RID &rid,int &off)
{
	char* data=new char[4096];
	char* iterator;
	int totFreeSpace;
	int toRet=-1;

	vector<freespaceObject> freeSpaceObj;

	fseek(fh.localFilePtr,(pagenum*4096),SEEK_SET);
	fread(&totFreeSpace,1,sizeof(int),fh.localFilePtr);

	//cout<<"Total free space in page "<<pagenum<<" is "<<totFreeSpace<<endl;
	if(totFreeSpace>=totlen)
	{
		fh.ReadPage(pagenum,data);

		int noSlots;
		int boundary;

		iterator=data+4092;
		memcpy(&noSlots,iterator,4);

		//cout<<"Number of slots in "<<pagenum<<" is "<<noSlots<<endl;
		boundary=4092-8*noSlots;
		//cout<<"Current boundary "<<boundary<<endl;
		int temp;
		iterator=data+4;
		memcpy(&temp,iterator,sizeof(int));
		iterator+=sizeof(int);
		//cout<<"Free slot location "<<temp<<endl;
		while(temp!=-1)
		{
			freespaceObject obj;
			obj.offset=temp;
			memcpy(&temp,iterator,sizeof(int));
			iterator+=sizeof(int);
			obj.chunksize=temp;
			//cout<<"Free chunk size for above slot "<<temp<<endl;
			freeSpaceObj.push_back(obj);
			memcpy(&temp,iterator,sizeof(int));
			iterator+=sizeof(int);
			//cout<<"Free slot location "<<temp<<endl;
		}

		int minChunk=3592;
		int minChunkIndex=-1;
		int lastIndex=-1;
		for(int i=0;i<freeSpaceObj.size();i++)
		{
			if(freeSpaceObj.at(i).chunksize<=minChunk && freeSpaceObj.at(i).chunksize>=totlen)
			{
				minChunk=freeSpaceObj.at(i).chunksize;
				minChunkIndex=i;
			}
			if((freeSpaceObj.at(i).chunksize+freeSpaceObj.at(i).offset)>=boundary)
				lastIndex=i;
		}
		//cout<<"Minimum chunk Index "<<minChunkIndex<<endl;
		//cout<<"Minimum chunk size "<<minChunk<<endl;
		//cout<<"Last chunk Index "<<lastIndex<<endl;
		if(minChunkIndex!=lastIndex && minChunkIndex!=-1)
			toRet=0;
		else if(minChunkIndex==lastIndex && totlen>=minChunk-40 && minChunkIndex!=-1)
		{
			int secondmin=4092;
			int secondminIndex=-1;
			for(int i=0;i<freeSpaceObj.size();i++)
			{
				if(freeSpaceObj.at(i).chunksize>minChunk && freeSpaceObj.at(i).chunksize>=totlen && freeSpaceObj.at(i).chunksize<secondmin)
				{
					secondminIndex=i;
					secondmin=freeSpaceObj.at(i).chunksize;
				}
			}
			if(secondminIndex==-1)
				toRet=-1;
			else
			{
				minChunk=secondmin;
				minChunkIndex=secondminIndex;
				toRet=0;
			}
		}
		else if(minChunkIndex==lastIndex && totlen<minChunk-120 && minChunkIndex!=-1)
			toRet=0;
		if(toRet==0)
		{
			off=freeSpaceObj.at(minChunkIndex).offset;
			totFreeSpace=totFreeSpace-totlen-8;
			//cout<<"Total free space after modification :"<<totFreeSpace<<endl;
			freeSpaceObj.at(minChunkIndex).offset+=totlen;
			//cout<<"new offset :"<<freeSpaceObj.at(minChunkIndex).offset<<endl;
			freeSpaceObj.at(minChunkIndex).chunksize=freeSpaceObj.at(minChunkIndex).chunksize-totlen;
			//cout<<"new chunksize :"<<freeSpaceObj.at(minChunkIndex).chunksize<<endl;
			freeSpaceObj.at(lastIndex).chunksize-=8;
			//cout<<"new chunksize at last chunk :"<<freeSpaceObj.at(lastIndex).chunksize<<endl;
			if(freeSpaceObj.at(minChunkIndex).chunksize==0)
				freeSpaceObj.erase(freeSpaceObj.begin()+minChunkIndex);
			if(lastIndex!=minChunkIndex && freeSpaceObj.at(lastIndex).chunksize==0)
				freeSpaceObj.erase(freeSpaceObj.begin()+lastIndex);
			rid.slotNum=noSlots;
			rid.pageNum=pagenum;

			noSlots++;
			iterator=data+4092;
			memcpy(iterator,&noSlots,sizeof(int));
			iterator=data+boundary-8;
			memcpy(iterator,&rid.slotNum,sizeof(int));
			iterator+=sizeof(int);
			memcpy(iterator,&off,sizeof(int));

			//cout<<"Slot information updated"<<endl;

			iterator=data;
			memcpy(iterator,&totFreeSpace,sizeof(int));
			iterator+=sizeof(int);
			for(int c=0;c<freeSpaceObj.size();c++)
			{
				memcpy(iterator,&freeSpaceObj.at(c).offset,sizeof(int));
				iterator+=sizeof(int);
				memcpy(iterator,&freeSpaceObj.at(c).chunksize,sizeof(int));
				iterator+=sizeof(int);
			}
			int temp=-1;
			memcpy(iterator,&temp,sizeof(int));

			//cout<<"Free Space information updated"<<endl;
			fh.WritePage(pagenum,data);
			pf->CloseFile(fh);

			delete[] data;
			return 0;
		}
		else
		{
			delete[] data;
			return -1;
		}

	}
	else
	{
		delete[] data;
		return -1;
	}
}


RC RM::insertTuple(const string tableName, const void *data, RID &rid)
{
	/**
	 *	Check if system catalog has entry for tableName
	 *	If yes,
	 *		Open tableName.dat file using pf
	 *		Get pagenum from rid and open that page in buffer
	 *
	 *		--- two options --
	 *		Option1: [ Query freespace manager to know where free space is
	 *		  		   Put *data into the hole
	 *		  		   Update slots information
	 *		  		   Update freespace manager ]
	 *
	 *		***OR***
	 *
	 *		Option2: [ Update the slot specified in rid with *data]
	 *		-----------------
	 *
	 *		Put the page back to file using writePage()
	 *		close file
	 *	Else
	 *		Show error that tableName doesn't exist
	 */

	vector<Attribute> attr;
	if(getAttributes(tableName, attr)==-1)
		return -1;
	//cout<<"received attributes of the table"<<endl;
	const void* offset=data;
	int datalength=0;

	for(int i=0;i<attr.size();i++)
	{
		if(attr.at(i).type==TypeInt)
		{
			datalength+=sizeof(int);
			offset+=sizeof(int);
		}
		else if(attr.at(i).type==TypeReal)
		{
			datalength+=sizeof(float);
			offset+=sizeof(float);
		}
		else if(attr.at(i).type==TypeVarChar)
		{
			int size;
			memcpy(&size,offset,sizeof(int));

			offset+=sizeof(int);
			datalength+=size;
			offset+=size;
		}
	}
	//cout<<"Data length: "<<datalength<<endl;
	int totlen=4+4*attr.size()+datalength;
	//cout<<"Total tuple length:"<<totlen<<endl;
	char* tuple=new char[totlen];
	char* buffer=new char[4096];
	char *iterator;
	int pageOffset;

	int currpage=-1;

	createRecord(tableName,data,tuple,totlen,attr);
	//cout<<"###Record created###"<<endl;
	pf->OpenFile(tableName.c_str(),fh);
	int numPages=fh.GetNumberOfPages();
	//cout<<"Number of pages in Employee table initially"<<endl;
	for(int i=0;i<numPages;i++)
	{
		if(ifRecordFitsInCurrPage(i,totlen,rid,pageOffset)==0)
		{
			pf->OpenFile(tableName.c_str(),fh);
			fh.ReadPage(i,buffer);
			currpage=i;
			break;
		}
	}
	if(currpage==-1)
	{
		initializeNewPage(tableName.c_str());
		pf->OpenFile(tableName.c_str(),fh);
		numPages=fh.GetNumberOfPages();
		currpage=numPages-1;
		fh.ReadPage(currpage,(void*)buffer);

		int temp=3584;
		iterator=buffer;
		memcpy(iterator,&temp,sizeof(int));
		iterator+=sizeof(int);

		temp=500+totlen;
		memcpy(iterator,&temp,sizeof(int));
		iterator+=sizeof(int);

		temp=3584;
		memcpy(iterator,&temp,sizeof(int));
		iterator+=sizeof(int);

		temp=-1;
		memcpy(iterator,&temp,sizeof(int));

		temp=0;
		iterator=buffer+4084;
		memcpy(iterator,&temp,sizeof(int));
		iterator+=sizeof(int);

		temp=500;
		memcpy(iterator,&temp,sizeof(int));
		iterator+=sizeof(int);

		temp=1;
		memcpy(iterator,&temp,sizeof(int));
	}

	iterator=buffer+pageOffset;
	memcpy(iterator,tuple,totlen);
	fh.WritePage(currpage,buffer);
	pf->CloseFile(fh);
	delete[] tuple;
	delete[] buffer;


	return 0;
}

RC RM::createRecord(const string tableName, const void *data,char* tuple,int totlen,vector<Attribute> attr)
{

		const void* offset=data;
		char *iteratorL=tuple;
		char *iteratorH=tuple+4+4*attr.size();

		int tl=totlen-4;

		memcpy(iteratorL,&tl,sizeof(int));
		iteratorL=iteratorL+sizeof(int);
		int baseLength=4*(attr.size()+1);
		//cout<<"base length: "<<baseLength<<endl;

		offset=data;

		for(int i=0;i<attr.size();i++)
		{
			baseLength-=4;
			if(attr.at(i).type==TypeInt)
			{
				//cout<<"base length "<<i<<baseLength<<endl;
				memcpy(iteratorL,&baseLength,sizeof(int));
				iteratorL+=sizeof(int);

				memcpy(iteratorH,offset,sizeof(int));
				baseLength+=sizeof(int);
				iteratorH+=sizeof(int);
				offset+=sizeof(int);
			}
			else if(attr.at(i).type==TypeReal)
			{
				//cout<<"base length "<<i<<baseLength<<endl;
				memcpy(iteratorL,&baseLength,sizeof(int));
				iteratorL+=sizeof(int);

				memcpy(iteratorH,offset,sizeof(float));
				baseLength+=sizeof(float);
				iteratorH+=sizeof(float);
				offset+=sizeof(float);
			}
			else if(attr.at(i).type==TypeVarChar)
			{
				//cout<<"base length "<<i<<baseLength<<endl;
				memcpy(iteratorL,&baseLength,sizeof(int));
				iteratorL+=sizeof(int);

				int size;
				memcpy(&size,offset,sizeof(int));
				offset+=sizeof(int);

				memcpy(iteratorH,offset,size);
				baseLength+=size;
				iteratorH+=size;
				offset+=size;
			}
		}

		return 0;

}

RC RM::deleteTuples(const string tableName)
{
	/**
	 *	Check if system catalog has entry for tableName
	 *	If yes,
	 *		Open tableName.dat file using pf
	 *		initialize the file to empty version
	 *		close file
	 *	Else
	 *		show error that tableName doesn't exist
	 */

	vector<Attribute> attrs;

	getAttributes(tableName,attrs);
	deleteTable(tableName);
	createTable(tableName,attrs);

	return 0;
}


RC RM::deleteTuple(const string tableName, const RID &rid)
{
	/**
	*	Check if system catalog has entry for tableName
	*	If yes,
	*		Open tableName.dat file using pf
	*		open the page specified by rid
	*		delete the record specified by slot id from rid
	*		Update slot information in the page
	*		Put the page back into file using writePage()
	*		Close the file
	*
	*	Else
	*		show error that tableName doesn't exist
	*/
	char *buffer=new char[4096];
	char* iterator;
	vector<freespaceObject> freeObj;

	if(isTableExists((char*)tableName.c_str())==-1)
	{
		//cout<<"table doesn't exist"<<endl;
		delete[] buffer;
		return -1;
	}

	pf->OpenFile(tableName.c_str(),fh);
	fh.ReadPage(rid.pageNum,buffer);

	iterator=buffer+4092;
	int totSlots;
	memcpy(&totSlots,iterator,sizeof(int));
	if(rid.slotNum>totSlots-1)
	{
		delete[] buffer;
		//cout<<"Record doesn't exist..."<<endl;
		pf->CloseFile(fh);
		return -1;
	}
	iterator=buffer+4096-(rid.slotNum+1)*8;
	int blocktoFree;
	int temp;
	memcpy(&temp,iterator,sizeof(int));
	if(temp==-1)
	{
		delete[] buffer;
		//cout<<"Record already deleted"<<endl;
		pf->CloseFile(fh);
		return -1;
	}
	else
	{
		temp=-1;
		memcpy(&blocktoFree,iterator,sizeof(int));
		//cout<<"Memory block to free resides at "<<blocktoFree<<endl;
		memcpy(iterator,&temp,sizeof(int));
	}
	iterator=buffer+blocktoFree;
	int lenblock;
	memcpy(&lenblock,iterator,sizeof(int));
	lenblock+=4;
	//cout<<"Size of Memory block to free is "<<lenblock<<endl;

	iterator=buffer;
	int currfreespace;
	memcpy(&currfreespace,iterator,sizeof(int));
	currfreespace+=lenblock;
	//cout<<"Size of total free space is "<<currfreespace<<endl;
	memcpy(iterator,&currfreespace,sizeof(int));
	iterator+=sizeof(int);

	//cout<<"#comes here#"<<endl;

	int freespaceinfo;
	memcpy(&freespaceinfo,iterator,sizeof(int));
	iterator+=sizeof(int);
	//cout<<"##comes here##"<<endl;
	while(freespaceinfo!=-1)
	{
		freespaceObject obj;
		obj.offset=freespaceinfo;
		memcpy(&freespaceinfo,iterator,sizeof(int));
		iterator+=sizeof(int);
		obj.chunksize=freespaceinfo;
		freeObj.push_back(obj);
		memcpy(&freespaceinfo,iterator,sizeof(int));
		iterator+=sizeof(int);
	}

	//cout<<"###comes here###"<<endl;
	int lowertomerge=-1;
	int uppertomerge=-1;
	for(int i=0;i<freeObj.size();i++)
	{
		if((blocktoFree+lenblock)==freeObj.at(i).offset)
		{
			lowertomerge=i;
		}
		if((freeObj.at(i).offset+freeObj.at(i).chunksize)==blocktoFree)
		{
			uppertomerge=i;
		}
	}
	//cout<<"####comes here####"<<endl;
	if(lowertomerge==-1 && uppertomerge!=-1)
	{
		//cout<<"111111"<<endl;
		freespaceObject newobj;
		newobj.offset=freeObj.at(uppertomerge).offset;
		newobj.chunksize=lenblock+freeObj.at(uppertomerge).chunksize;
		//cout<<"New merged chunk starts at "<<newobj.offset<<endl;
		//cout<<"New merged chunk size "<<newobj.chunksize<<endl;

		freeObj.erase(freeObj.begin()+uppertomerge);
		freeObj.push_back(newobj);
	}
	else if(lowertomerge!=-1 && uppertomerge==-1)
	{
		//cout<<"222222"<<endl;
		freespaceObject newobj;
		newobj.offset=blocktoFree;
		newobj.chunksize=lenblock+freeObj.at(lowertomerge).chunksize;
		//cout<<"New merged chunk starts at "<<newobj.offset<<endl;
		//cout<<"New merged chunk size "<<newobj.chunksize<<endl;
		freeObj.erase(freeObj.begin()+lowertomerge);
		freeObj.push_back(newobj);
	}
	else if(lowertomerge!=-1 && uppertomerge!=-1)
	{
		//cout<<"333333"<<endl;
		freespaceObject newobj;
		newobj.offset=freeObj.at(uppertomerge).offset;
		newobj.chunksize=lenblock+freeObj.at(lowertomerge).chunksize+freeObj.at(uppertomerge).chunksize;
		//cout<<"New merged chunk starts at "<<newobj.offset<<endl;
		//cout<<"New merged chunk size "<<newobj.chunksize<<endl;
		if(lowertomerge<uppertomerge)
		{
			freeObj.erase(freeObj.begin()+uppertomerge);
			freeObj.erase(freeObj.begin()+lowertomerge);
		}
		else if(lowertomerge>uppertomerge)
		{
			freeObj.erase(freeObj.begin()+lowertomerge);
			freeObj.erase(freeObj.begin()+uppertomerge);
		}
		else
		{
			freeObj.erase(freeObj.begin()+lowertomerge);
		}
		freeObj.push_back(newobj);
	}
	else
	{
		//cout<<"444444"<<endl;
		freespaceObject newobj;
		newobj.offset=blocktoFree;
		newobj.chunksize=lenblock;
		freeObj.push_back(newobj);
		//cout<<"New chunk starts at "<<newobj.offset<<endl;
		//cout<<"New chunk size "<<newobj.chunksize<<endl;
	}
	//cout<<"#####comes here#####"<<endl;
	iterator=buffer+sizeof(int);
	for(int i=0;i<freeObj.size();i++)
	{
		memcpy(iterator,&freeObj.at(i).offset,sizeof(int));
		iterator+=sizeof(int);
		memcpy(iterator,&freeObj.at(i).chunksize,sizeof(int));
		iterator+=sizeof(int);
	}

	int c=-1;
	memcpy(iterator,&c,sizeof(int));
	//cout<<"######comes here######"<<endl;
	fh.WritePage(rid.pageNum,buffer);
	pf->CloseFile(fh);
	delete[] buffer;
	return 0;
	//return -1;
}


RC RM::updateTuple(const string tableName, const void *data, RID &rid)
{
	/**
	*	Check if system catalog has entry for tableName
	*	If yes,
	*		Open tableName.dat file using pf
	*		open the page specified by rid
	*		update/replace the record specified by slot id from rid
	*		Update slot information in the page
	*		Put the page back into file using writePage()
	*		Close the file
	*
	*	Else
	*		show error that tableName doesn't exist
	*/
	RID newRID;
	assert(deleteTuple(tableName,rid)==0);

	assert(insertTuple(tableName,data,newRID)==0);
	rid.pageNum=newRID.pageNum;
	rid.slotNum=newRID.slotNum;
	return 0;
}


RC RM::readTuple(const string tableName, const RID &rid, void *data)
{
	/**
	*	Check if system catalog has entry for tableName
	*	If yes,
	*		Open tableName.dat file using pf
	*		open the page specified by rid
	*		Read the slot specified by rid, put into *data
	*		Close the file
	*
	*	Else
	*		show error that tableName doesn't exist
	*/

	vector<Attribute> attr;
	char *buffer=new char[4096];
	char *iterator;

	if(getAttributes(tableName,attr)==-1)
	{
		delete[] buffer;
		return -1;
	}

	pf->OpenFile(tableName.c_str(),fh);

	int pageToRead=rid.pageNum;
	int slotID=rid.slotNum;
	//cout<<pageToRead;
	//cout<<slotID;

	fh.ReadPage(pageToRead,buffer);
	//cout<<"Page in new file read"<<endl;

	int totalSlots;

	iterator=buffer+4092;
	memcpy(&totalSlots,iterator,sizeof(int));
	//cout<<"total slots: "<<totalSlots<<endl;

	if(slotID>totalSlots-1)
	{
		delete[] buffer;
		pf->CloseFile(fh);
		return -1;
	}
	iterator=buffer+(4096-(slotID+1)*8);

	int pageoffset;
	memcpy(&pageoffset,iterator,sizeof(int));
	if(pageoffset==-1)
	{
		pf->CloseFile(fh);
		delete[] buffer;
		return -1;

	}
	iterator=buffer+pageoffset;
	int recordLength;
	memcpy(&recordLength,iterator,sizeof(int));
	iterator+=sizeof(int);
	//cout<<"record length: "<<recordLength<<endl;

	char* iteratorH=iterator+4*attr.size();

	int tupleLength=recordLength-4*attr.size();
	for(int i=0;i<attr.size();i++)
	{
		if(attr.at(i).type==TypeVarChar)
		{
			tupleLength+=4;
		}
	}
	//cout<<"tuple length"<<tupleLength<<endl;

	char *offset=(char*)data;

	for(int i=0;i<attr.size();i++)
	{
		if(attr.at(i).type==TypeInt)
		{
			int t;
			memcpy(offset,iteratorH,sizeof(int));
			memcpy(&t,iteratorH,sizeof(int));
			//cout<<"para copied: "<<t<<endl;
			offset+=sizeof(int);
			iteratorH+=sizeof(int);
			iterator+=sizeof(int);

		}
		else if(attr.at(i).type==TypeReal)
		{
			float l;
			memcpy(offset,iteratorH,sizeof(float));
			memcpy(&l,iteratorH,sizeof(float));
			printf("Height copied:%f\n",l);
			offset+=sizeof(float);
			iteratorH+=sizeof(float);
			iterator+=sizeof(float);
		}
		else if(attr.at(i).type==TypeVarChar)
		{
			int low;
			memcpy(&low,iterator,sizeof(int));

			iterator+=sizeof(int);
			int high;
			memcpy(&high,iterator,sizeof(int));
			high=high-low+4;
			//cout<<"length of name "<<high<<endl;
			memcpy(offset,&high,sizeof(int));
			offset+=sizeof(int);
			memcpy(offset,iteratorH,high);
			char *str=new char[high];
			memcpy(str,iteratorH,high);
			//cout<<"string copied: "<<str;
			delete[] str;

			offset+=high;
			iteratorH+=high;
		}
	}
	//cout<<"comes here"<<endl;
	//data=tuple;
	pf->CloseFile(fh);
	delete[] buffer;
	return 0;

}

RC RM::readAttribute(const string tableName, const RID &rid, const string attributeName, void *data)
{
	/**
	*	Check if system catalog has entry for tableName
	*	If yes,
	*		Get the attribute information for tableName, store in vector
	*		Open tableName.dat file using pf
	*		open the page specified by rid
	*		Read the record specified by slot id in some buffer
	*		Iterate over vector and the buffer to read required attribute
	*		Copy the attribute to *data, use lenght information from system catalog (for varchar lenght stored in the record itself)
	*		Close the file
	*
	*	Else
	*		show error that tableName doesn't exist
	*/

	vector<Attribute> attr;
	char* buffer=new char[4096];
	char* iterator;

	if(getAttributes(tableName,attr)==-1)
	{	delete[] buffer;
		return -1;
	}
	int attrPos;
	for(int i=0;i<attr.size();i++)
	{
		if(attr.at(i).name==attributeName)
		{
			attrPos=i;
			break;
		}
	}
	//cout<<"Attr Pos "<<attrPos<<endl;
	pf->OpenFile(tableName.c_str(),fh);
	fh.ReadPage(rid.pageNum,buffer);
	pf->CloseFile(fh);

	iterator=buffer+4092;
	int totSlots;
	memcpy(&totSlots,iterator,sizeof(int));
	if(rid.slotNum>totSlots-1)
	{
		delete[] buffer;
		return -1;
	}
	iterator=buffer+4096-(rid.slotNum+1)*8;

	int offset;
	memcpy(&offset,iterator,sizeof(int));
	//cout<<"offset in page: "<<offset<<endl;
	iterator=buffer+offset+sizeof(int);

	iterator+=(attrPos)*4;
	int bytestoskip;
	memcpy(&bytestoskip,iterator,sizeof(int));

	//cout<<"Bytes to skip "<<bytestoskip<<endl;

	if(attr.at(attrPos).type==TypeVarChar)
	{
		iterator+=sizeof(int);
		int nextPtr;
		memcpy(&nextPtr,iterator,sizeof(int));
		int len=nextPtr-bytestoskip+sizeof(int);
		iterator=iterator+bytestoskip-sizeof(int);
		char *ptr=new char[len];


		memcpy(data,iterator,len);
		memcpy(ptr,data,len);
		//cout<<"read string: "<<ptr<<endl;
		delete[] ptr;
		delete[] buffer;
		return 0;
	}
	else if(attr.at(attrPos).type==TypeReal)
	{
		iterator=iterator+bytestoskip;
		memcpy(data,iterator,sizeof(float));
		float para;
		memcpy(&para,data,sizeof(float));
		//cout<<"Parameter read: "<<para<<endl;
		delete[] buffer;
		return 0;
	}
	else
	{
		iterator=iterator+bytestoskip;
		memcpy(data,iterator,sizeof(int));
		int para;
		memcpy(&para,data,sizeof(int));
		//cout<<"Parameter read: "<<para<<endl;
		delete[] buffer;
		return 0;

	}

	return -1;
}


RC RM::reorganizePage(const string tableName, const unsigned pageNumber)
{
	/**
	*	Check if system catalog has entry for tableName
	*	If yes,
	*		Open tableName.dat file using pf
	*		open the page specified by rid
	*		Find first free slot in page, move all records up
	*		Find next free slot, move all records up
	*		Continue till the end of page
	*		While doing reorganizing this keep the slot information in some buffer
	*		Update slot information in the page using the buffer
	*		Put the page back into file using writePage()
	*		Close the file
	*
	*	Else
	*		show error that tableName doesn't exist
	*/
	char* buffer=new char[4096];
	char* bIterator;
	char* newbuffer=new char[4096];
	char* nIterator;
	pf->OpenFile(tableName.c_str(),fh);
	fh.ReadPage(pageNumber,buffer);

	int noSlots;

	bIterator=buffer+4092;
	memcpy(&noSlots,bIterator,sizeof(int));
	nIterator=newbuffer+4092;
	memcpy(nIterator,&noSlots,sizeof(int));

	int slotID;
	int slotOff;
	int newOff=500;
	int newFree=3592;
	char* nNextPtr=newbuffer+500;

	for(int i=0;i<noSlots;i++)
	{
		bIterator-=8;
		memcpy(&slotID,bIterator,sizeof(int));
		bIterator+=sizeof(int);
		memcpy(&slotOff,bIterator,sizeof(int));
		bIterator-=sizeof(int);

		nIterator-=8;
		memcpy(nIterator,&slotID,sizeof(int));
		nIterator+=sizeof(int);
		memcpy(nIterator,&slotOff,sizeof(int));
		nIterator-=sizeof(int);

		newFree-=8;
		if(slotOff!=-1)
		{
			char *btemp=bIterator;

			bIterator=buffer+slotOff;
			int blocklen;
			memcpy(&blocklen,bIterator,sizeof(int));
			blocklen+=sizeof(int);
			memcpy(nNextPtr,bIterator,blocklen);
			nNextPtr+=blocklen;
			newOff+=blocklen;
			newFree-=blocklen;

			bIterator=btemp;
		}
	}
	nIterator=newbuffer;
	memcpy(nIterator,&newFree,sizeof(int));
	nIterator+=sizeof(int);
	memcpy(nIterator,&newOff,sizeof(int));
	nIterator+=sizeof(int);
	memcpy(nIterator,&newFree,sizeof(int));
	nIterator+=sizeof(int);
	int temp=-1;
	memcpy(nIterator,&temp,sizeof(int));

	fh.WritePage(pageNumber,newbuffer);

	delete[] buffer;
	delete[] newbuffer;
	pf->CloseFile(fh);
	return 0;


	/*pf->OpenFile(tableName.c_str(), fh);

	void* data = malloc(4096); // original page data
	void* newdata = malloc(4096); // reformatted page data

	char* newDataPtr = (char*)newdata + 500; // pointer from where we will store records
	int newOffset = 0; // offset in the new page
	char* newP = (char*)newdata + 4092; // tail pointer in new page
	fh.ReadPage(pageNumber, data); // read page in *data
	int i;
	char* p = (char*)data;
	p += 4092; // go to the last 4 bytes, i.e. number of slots
	int totalSlots = *((int*) p); // get the number of slots
	int newSlots = 0;
	int slotNum;
	int slotOffset;
	int tupleLen;
	for (i = 0; i < totalSlots; i++) {
	slotNum = *((int*) (p - 8)); // get the number of slot
	slotOffset = *((int*) (p - 4)); // get the offset of slot in original page
	p -= 8; // proceed to next slot
	if (slotOffset != -1) { // slot is copied to new page only if it is valid, i.e. its offset it not -1
	// invalid/deleted slots have offset -1
	tupleLen = *(((int*) ((char*)data + slotOffset))); // get the lenght of the tuple starting @slotOffset in the original page
	//cout<<endl<<"tupleLen="<<tupleLen<<endl;
	memcpy((void*)newDataPtr, ((void*)((char*)data + slotOffset)), tupleLen+4); // copy the record to new page
	memcpy((void*)(newP - 8), &slotNum, 4); // copy the slot number to new page
	memcpy((void*)(newP - 4), &newOffset, 4); // copy the new offset in the new page as slot information
	newSlots++; // added one record!
	newDataPtr += tupleLen; // move forward in the memory
	newOffset += tupleLen; // increase offset
	newP -= 8; // proceed to next slot, slot info grows backwards
	}
	}
	/**
	* 500 bytes reserved for free space management
	* 4 bytes for free space in new page
	* 4 bytes for free space offset in new page
	* 4 bytes for no. of slots in new page
	* for every slot, 4 bytes for slot number, 4 bytes for offset
	*/
	/*int freespace = 4096 - 500 - 4 - 4 - 4 - (newSlots * (4 + 4));
	memcpy((void*)((char*)newdata + 4092), &newSlots, 4); // copy no. of slots to new page
	memcpy((void*)((char*)newdata + 4), &freespace, 4); // copy free space size information to new page
	memcpy((void*)((char*)newdata + 8), &newOffset, 4); // copy free space offset information to new page

	fh.WritePage(pageNumber, newdata); // write the reorganized page to file/table
	pf->CloseFile(fh); // close the file
	return 0;*/
}


RC RM_ScanIterator::getNextTuple(RID &rid, void *data)
{
if(i >= 0)
{
scanObj next = v.at(i);
memcpy(data, &next.attr, 4);
i--;
return 0;
}
else
{
return RM_EOF;
}
}
RC RM::scan(const string tableName,
      const vector<string> &attributeNames,
      RM_ScanIterator &rm_ScanIterator)
{
void* tuple = malloc(1000);
PF_FileHandle fh;
pf->OpenFile(tableName.c_str(), fh);
int numPages = fh.GetNumberOfPages();
void* page = malloc(4096);
for(int i = 0; i<numPages; i++)
{
		fh.ReadPage(i, page);
		int numSlots = *((int*) ((char*) page + 4092));
		//cout << "\n****numSlots" << numSlots;
		int offset = 4096 - 4 - (2 * 4 * numSlots);
		for (int j = offset; j < 4092; j += 8) {
			int slotOffset = *((int*) ((char*) page + j + 4));
			if (slotOffset != -1) {
				int slotNum = *((int*) ((char*) page + j));
				RID rid;
				rid.pageNum = i;
				rid.slotNum = slotNum;
				_rm->readTuple(tableName, rid, tuple);

				int tupleoffset = 0;

				int name_length;
				memcpy(&name_length, (char *) tuple + tupleoffset, sizeof(int));
				tupleoffset += sizeof(int);
				//memcpy((char *)buffer + offset, name.c_str(), name_length);
				tupleoffset += name_length;
				int age;
				memcpy(&age, (char *) tuple + tupleoffset, sizeof(int));

				scanObj obj;
				obj.page = rid.pageNum;
				obj.slot = rid.slotNum;
				obj.attr = age;
				rm_ScanIterator.v.push_back(obj);
			}
		}
}
rm_ScanIterator.i = rm_ScanIterator.v.size()-1;
free(page);
free(tuple);
pf->CloseFile(fh);
return 0;
}


/**
 * Optional part starts
 */

RC RM::dropAttribute(const string tableName, const string attributeName)
{
	return -1;
}

RC RM::addAttribute(const string tableName, const Attribute attr)
{
	return -1;
}


RC RM::reorganizeTable(const string tableName)
{
	return -1;
}


RC RM::scan(const string tableName,
      const string conditionAttribute,
      const CompOp compOp,                  // comparision type such as "<" and "="
      const void *value,                    // used in the comparison
      const vector<string> &attributeNames, // a list of projected attributes
      RM_ScanIterator &rm_ScanIterator)
{
	return -1;
}

